package ac.il.technion.c236369.hw4.core;

import java.io.IOException;
import java.util.List;
import java.util.Map;

import org.apache.http.HttpException;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.DefaultHttpResponseFactory;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.protocol.BasicHttpProcessor;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.ResponseContent;
import org.apache.http.protocol.ResponseDate;
import org.apache.http.protocol.ResponseServer;
import org.apache.log4j.Logger;

import ac.il.technion.c236369.hw4.configuration.ServerConfig;
import ac.il.technion.c236369.hw4.core.protocol.DefaultRequestHandler;
import ac.il.technion.c236369.hw4.core.protocol.HttpService;
import ac.il.technion.c236369.hw4.core.protocol.InternalErrorReport;
import ac.il.technion.c236369.hw4.core.protocol.RequestRegistry;
import ac.il.technion.c236369.hw4.core.protocol.SpecifiedRequetHandler;
import ac.il.technion.c236369.hw4.core.queues.ServerRequestsQueue;
import ac.il.technion.c236369.hw4.typehandlers.TypeHandler;

public class RequestHandler extends ServerRunnableEntity{
	
	private HttpService service;

	public RequestHandler( ) {
		final BasicHttpParams params = new BasicHttpParams( );
		setUpLogger( Logger.getLogger(RequestHandler.class));
		params
			.setIntParameter( CoreConnectionPNames.SO_TIMEOUT, 5000)
			.setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024)
			.setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false);
		BasicHttpProcessor processor = new BasicHttpProcessor( );
		processor.addInterceptor( new ResponseDate( ));
		processor.addInterceptor( new ResponseContent( ));
		processor.addInterceptor( new ResponseServer( ));
		// Send to client connection closed notification
		processor.addInterceptor( new HttpResponseInterceptor() {
			@Override
			public void process(HttpResponse response, HttpContext contex)
					throws HttpException, IOException {
				response.setHeader( HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE);
			}
		});
		
		RequestRegistry registry = new RequestRegistry( new DefaultRequestHandler( ));
		Map<String, List<String>> handlers = ServerConfig.getInstance().getProperties().getTypeHandlers( );
		for ( String className : handlers.keySet( )) {
			for ( String $ : handlers.get( className)) {
				try {
					TypeHandler handlerClass = (TypeHandler)Class.forName(className).newInstance( );
					SpecifiedRequetHandler handler = new SpecifiedRequetHandler(handlerClass);
					registry.register( "*." + $, handler);
					if ( log.isDebugEnabled( ))
						log.debug( "Load type handler: + <" + className + "> for file extension: <" + $ +">");
				} catch (Exception e) {
					log.error(e);
				}
			}
		}
		service = new HttpService( processor, 
					new DefaultConnectionReuseStrategy(),
					new DefaultHttpResponseFactory( ));
		service.setParams(params);
		service.setHandlerResolver(registry);
	}
	
	@Override
	public void runImpl( ) {
		ServerRequest request = ServerRequestsQueue.getInstance().get();
		if ( request == ServerRequestsQueue.NULL_SERVER_REQUEST)
			return;
		try {
			log.info( "Serving request from: " + request.getConnection().getRemoteAddress( ));
			service.handleRequest( request.getConnection(), request.getContext( ));
			if ( request.getConnection( ).isOpen( ))
				request.getConnection().flush( );
			request.getConnection().shutdown( );
		} catch (Exception e) {
			log.error( e);
			try {
				new InternalErrorReport( request.getConnection( )).sendErrorReport( );
			} catch (IOException e1) {
				log.error(e);
			}
		}
	}
}
